if (!gBaseURI) {
    var gBaseURI = "http://localhost:8080";
}
if (!adminContextRoot) {
    var adminContextRoot = "administrator";
}

if (!gUserName) {
    var gUserName = "Debug User";
}

/**
 * The top-level package.
 */
jsx3.lang.Package.definePackage("tibco", function(tibco) {
});

/**
 * The root package for all ActiveMatrix Admin UI classes.
 */
jsx3.lang.Package.definePackage("tibco.admin", function(admin) {
});

/**
 * The main utility class of the ActiveMatrix Admin UI, containing most of the utility functions
 * to be used by plugin developers.
 */
jsx3.lang.Class.defineClass("tibco.admin.Main", null, null, function(main) {

    /**
     * {Object} An associative array containing the mappings between plugin package namespaces and their contextRoots
     * @private
     * @jsxobf-clobber
     */
    main.contextRoots = new Object();

    /**
     * {jsx3.app.Server} The current server object.  This allows individual plugin projects to set the server object when not being run
     * from the administrator project.
     * @private
     * @jsxobf-clobber
     */
    main.server = null;

    /**
     * This is called upon deserialization of the Main UI component (the AdminDesktop).  It gets the list of installed
     * plugins from the server, and registers their namespace/contextRoot associations.
     *
     * @param target {Object} the target object on which to perform the response callback notifying the caller that the
     *                        operation is complete
     * @param fctn {function} the function to call on <code>target</code> to perform the response callback
     *
     * @private
     */
    main.initializePlugins = function(target, fctn) {
        this.contextRoots['tibco.admin'] = adminContextRoot;
        this.callgetAllInstalledPluginContext(target, fctn);
    }

    /**
     * Utility function to get the name of the currently-logged-in user.
     *
     * @return {String} the name of the logged in user
     */
    main.getUserName = function() {
        return gUserName;
    }

    /**
     * Given a filename or path, resolves the URI to an image in the MatrixAdmin addin.
     *
     * @param strPath {String} the filename (must be in the images/matrix subfolder of the addin) or the path within the
     *                         MatrixAdmin addin to the desired image file
     *
     * @return {jsx3.net.URI} the URI to the image file requested
     */
    main.resolveAddinImage = function(strPath) {
        var path = strPath;
        var index = path.indexOf("/");
        if (index != 0) {
            path = "/" + path;
        }
        index = path.indexOf("/images/matrix");
        if (index != 0) {
            path = "/images/matrix" + path;
        }
        path = "jsxaddin://user!MatrixAdmin" + path;
        return this.getServer().resolveURI(path);
    }

    /**
     * Given a filename or path, resolves the URI to an image in the ActiveMatrix webapp contextRoot (the launch page).
     * This is the value you would use to access an image outside of GI (for example when setting the <code>src</code>
     * attribute of an <code>&lt;img&gt;</code> tag.
     *
     * @param strPath {String} the resolved (relative to the project) path MatrixAdmin addin to the desired image file
     *
     * @return {String} the path to the image file requested, relative to the ActiveMatrix webapp contextRoot (the
     *                  location of the browser document)
     */
    main.resolveImageRelativeToAdmin = function(strPath) {
        jsx3.log("Resolving: " + strPath);
        var path = "" + strPath;
        var idx = path.indexOf("addins");
        if(idx == -1) {
            path = "" + this.getServer().resolveURI(path);
            idx = path.indexOf("addins");
            if(idx == -1) {
                jsx3.log("Returning orig: " + strPath);
                return strPath;
            }
        }
        jsx3.log("Returning resolved: ../../" + path.substring(idx));
        return "../../" + path.substring(idx);
    }

    /**
     * Internal function. Performs a service call to get all implementing plugin components for some given interfaces.
     *
     * @param target {Object} the target object on which to perform the response callback
     * @param fctn {function} the function to call on <code>target</code> to perform the response callback
     *
     * @private
     * @jsxobf-clobber
     */
    main.callgetInstalledPlugins = function(target, fctn) {
        try {

            jsx3.require("jsx3.net.Service");
            var objService = main.loadService("tibco.admin", "rules/getInstalledPlugins.xml", "getInstalledPlugins");
            if (this.getServer().getEnv("MODE")) {
                objService.setEndpointURL(tibco.admin.util.ServiceHelper.getPluginsEndpointURL());
            }
            objService.callbackTarget = target;
            objService.callbackFunction = fctn;
            //subscribe and call
            objService.subscribe(jsx3.net.Service.ON_SUCCESS, this.ongetInstalledPluginsSuccess);
            objService.subscribe(jsx3.net.Service.ON_ERROR, this.ongetInstalledPluginsError);
            objService.subscribe(jsx3.net.Service.ON_INVALID, this.ongetInstalledPluginsInvalid);
            objService.doCall();
        }
        catch(ex) {
            this.logException(ex, "Error in tibco.admin.Main.callgetInstalledPlugins");
        }

    };

    /**
     * Internal function. Called when a successful service call has been made to get implementing plugin components
     * for a given list of interfaces. Calls register plugins, which then notifies the requesting plugin container
     * of the plugins via a callback.
     *
     * @param objEvent {Object} the response object from the service call.
     * @private
     * @jsxobf-clobber
     */
    main.ongetInstalledPluginsSuccess = function(objEvent) {
        jsx3.log("SUCCESS!");
        try {
            var doc = main.getServer().getCache().getDocument("getInstalledPlugins_00");
            if (doc) {
                var iter = doc.getChildNodes().iterator();
                while (iter.hasNext()) {
                    var next = iter.next();
                    var namespace = next.getAttribute("packageNamespace");
                    var contextRoot = next.getAttribute("contextRoot");
                    main.contextRoots[namespace] = contextRoot;
                }
            }
            objEvent.target.callbackFunction.call(objEvent.target.callbackTarget);
        }
        catch(ex) {
            main.logException(ex);
        }
    };

    /**
     * Internal function. Called when an error occurs in the service call to get implementing plugin components
     * for a given list of interfaces. Notifies the requesting plugin container of the error by returning <code>null</code>
     * in the callback.
     *
     * @param objEvent {Object} the response object from the service call.
     * @private
     * @jsxobf-clobber
     */
    main.ongetInstalledPluginsError = function(objEvent) {
        jsx3.log("ERROR getting installed plugins!");
    };


    /**
     * Internal function. Called when an error occurs in the service call to get implementing plugin components
     * for a given list of interfaces, due to the mapping being invalid. Notifies the requesting plugin container
     * of the error by returning <code>null</code> in the callback.
     *
     * @param objEvent {Object} the response object from the service call.
     * @private
     * @jsxobf-clobber
     */
    main.ongetInstalledPluginsInvalid = function(objEvent) {
        jsx3.log("INVALID! Document invalid while getting installed plugins!");
    };


   /**
     * Internal function. Performs a service call to get all implementing plugin components for some given interfaces.
     *
     * @param target {Object} the target object on which to perform the response callback
     * @param fctn {function} the function to call on <code>target</code> to perform the response callback
     *
     * @private
     * @jsxobf-clobber
     */
    main.callgetAllInstalledPluginContext = function(target, fctn) {
        try {
            var objService = main.loadService("tibco.admin", "rules/getInstalledPluginContexts.xml", "getAllInstalledPluginContexts");
            if (this.getServer().getEnv("MODE")) {
                objService.setEndpointURL(tibco.admin.util.ServiceHelper.getPluginsEndpointURL());
            }
            objService.callbackTarget = target;
            objService.callbackFunction = fctn;
            //subscribe and call
            objService.subscribe(jsx3.net.Service.ON_SUCCESS, this.ongetAllInstalledPluginContextsSuccess);
            objService.subscribe(jsx3.net.Service.ON_ERROR, this.ongetAllInstalledPluginContextsError);
            objService.subscribe(jsx3.net.Service.ON_INVALID, this.ongetAllInstalledPluginContextsInvalid);
            objService.doCall();
        }
        catch(ex) {
            this.logException(ex, "Error in tibco.admin.Main.callgetInstalledPlugins");
        }
    };

     /**
     * Internal function. Called when a successful service call has been made to get implementing plugin components
     * for a given list of interfaces. Calls register plugins, which then notifies the requesting plugin container
     * of the plugins via a callback.
     *
     * @param objEvent {Object} the response object from the service call.
     * @private
     * @jsxobf-clobber
     */
    main.ongetAllInstalledPluginContextsSuccess = function(objEvent) {
        jsx3.log("SUCCESS!");
        try {
            var doc = main.getServer().getCache().getDocument("getAllInstalledPluginContexts_00");
            if (doc) {
                var iter = doc.getChildNodes().iterator();
                while (iter.hasNext()) {
                    var next = iter.next();
                    var namespace = next.getAttribute("packageNamespace");
                    var contextRoot = next.getAttribute("contextRoot");
                    main.contextRoots[namespace] = contextRoot;
                }
            }
            objEvent.target.callbackFunction.call(objEvent.target.callbackTarget);
        }
        catch(ex) {
            main.logException(ex);
        }
    };

    /**
     * Internal function. Called when an error occurs in the service call to get implementing plugin components
     * for a given list of interfaces. Notifies the requesting plugin container of the error by returning <code>null</code>
     * in the callback.
     *
     * @param objEvent {Object} the response object from the service call.
     * @private
     * @jsxobf-clobber
     */
    main.ongetAllInstalledPluginContextsError = function(objEvent) {
        alert("error")
        jsx3.log("ERROR getting installed plugins!");
    };

   /**
     * Internal function. Called when an error occurs in the service call to get implementing plugin components
     * for a given list of interfaces, due to the mapping being invalid. Notifies the requesting plugin container
     * of the error by returning <code>null</code> in the callback.
     *
     * @param objEvent {Object} the response object from the service call.
     * @private
     * @jsxobf-clobber
     */
    main.ongetAllInstalledPluginContextsInvalid = function(objEvent) {
          jsx3.log("INVALID! Document invalid while getting installed plugins!");
    };





    /**
     * Gets the application server for the ActiveMatrix Admin UI
     *
     * @return {jsx3.app.Server} the application server object.
     * @throws {tibco.admin.exception.ServerNotDefinedException} if no valid {jsx3.app.Server} object has been
     *         set
     */
    main.getServer = function() {
        if (this.server) {
            return this.server;
        }
        else {
            if (APP && APP.tibco && APP.tibco.admin && (APP.tibco.admin instanceof jsx3.app.Server)) {
                return APP.tibco.admin;
            }
            else {
                throw new tibco.admin.exception.ServerNotDefinedException(" a call to tibco.admin.Main.setServer() is required.");
            }
        }
    };

    /**
     * NOTE: In ActiveMatrix 2.0, this function does NOT work, as the server is set before the addin is fully loaded (to
     *       resolve paths from the addin's dynamic property files). Will remain private until such time as this issue can be resolved.
     *
     * Allows plugin developers to programmatically set the server object for use in their project, if they are not running their
     * plugin from inside the administrator project.  The first call to this function will set the server object.  Successive calls
     * will be ignored.  In this fashion, the top-level project will set the server object, and code executed from dynamically
     * loaded plugins (from other projects) will not be able to incorrectly set this value when their project is not defining the
     * server object.
     *
     * @param serverObj {String | jsx3.app.Server} either the <code>jsx3.app.Server</code> object to use as the server context for
     *                  the application, of a string giving the name of the object (an eval() will be performed on this string to
     *                  get the <code>jsx3.app.Server</code> object).
     * @throws {tibco.admin.exception.InvalidServerException} if <code>serverObj</code> is not a valid <code>jsx3.app.Server</code>
     *         object or a <code>String</code> that can be evaluated to a valid <code>jsx3.app.Server</code> object.
     * @throws {jsx3.lang.Exception} if any unknown error occurs while setting the server object.  Known errors will always be
     *         displayed in an alert.
     *
     * @private
     */
    main.setServer = function(serverObj) {
        try {
            if (!this.server) {
                if (serverObj) {
                    if (typeof(serverObj) == "string") {
                        try {
                            this.server = jsx3.eval(serverObj);
                            if (!(this.server instanceof jsx3.app.Server)) {
                                this.server = null;
                                throw new tibco.admin.exception.InvalidServerException("The string " + serverObj + " cannot be evaluated to a valid jsx3.app.Server object");
                            }
                        } catch(ex) {
                            if (ex instanceof tibco.admin.exception.InvalidServerException) {
                                throw ex;
                            }
                            else {
                                this.server = null;
                                throw new tibco.admin.exception.InvalidServerException("Error evaluating " + serverObj + ". Cannot get a valid jsx3.app.Server object", jsx3.NativeError.wrap(ex));
                            }
                        }
                    }
                    else if (serverObj instanceof jsx3.app.Server) {
                        this.server = serverObj;
                    }
                    else {
                        throw new tibco.admin.exception.InvalidServerException("The argument to tibco.admin.Main.setServer() must be either a jsx3.app.Server object or a String");
                    }
                }
                else {
                    throw new tibco.admin.exception.InvalidServerException("The argument to tibco.admin.Main.setServer() is null or undefined");
                }
            }
        } catch(ex) {
            this.logException(ex, "Error in tibco.admin.Main.setServer");
            if (ex instanceof tibco.admin.exception.InvalidServerException) {
                throw ex;
            }
            else {
                throw new jsx3.lang.Exception("Unknown exception while setting server object in tibco.admin.Main.setServer()", jsx3.NativeError.wrap(ex));
            }
        }
        //        try {
        //            if (!this.isFileSystem()) {
        //                if (this.getServer().getEnv("MODE")) {
        //                     do service call when we have the wsdl, for now make it static
        //                    this.contextRoots['tibco.admin'] = adminContextRoot;
        //                    this.contextRoots['tibco.policymgr'] = "policymgr";
        //                }
        //                else {
        //                    this.contextRoots['tibco.admin'] = adminContextRoot;
        //                    this.contextRoots['tibco.policymgr'] = "policymgr";
        //                }
        //            }
        //        }
        //        catch(ex) {
        // do nothing...this indicates that the server is not yet set, so the context roots cannot be initialized yet
        //        }
    }

    /**
     * Gets a url that is resolved to the proper value for a static or live environment from for the current protocol
     * (file, http, etc.), given the desired resource path and the resource's plugin package's namespace.
     *
     * @param namespace {String | tibco.admin.util.component.ComponentConfig} the plugin package namespace of this
     *                  resource's plugin package, or the <code>ComponentConfig</code> of the component that owns the
     *                  resource at <code>relPath</code>
     * @param relPath {String} the path to the resource, relative to the plugin's context root
     *
     * @throws {tibco.admin.exception.InvalidNamespaceException} if <code>namespace</code> is invalid
     * @throws {jsx3.lang.Exception} if an unknown error occurs
     *
     * @return {String} the resolved URI
     */
    main.resolveURI = function(namespace, relPath) {
        try {
            var id = tibco.admin.util.Performance.beginBenchmark("Resolving URI for " + relPath);
            var ret = tibco.admin.util.component.ComponentUtils.resolveURI(namespace, relPath);
            tibco.admin.util.Performance.endBenchmark(id);
            return ret;
        } catch(ex) {
            throw ex;
        }
    }

    /**
     * Gets the appropriate base URI, depending on whether the application is running in from the filesystem or not.
     *
     * @return {String} the application's base URI
     * @throws {jsx3.lang.Exception} if an error occurs while calculating the gBaseURI
     */
    main.getGBaseURI = function() {
        try {
            var base = this.isFileSystem() ? this.getGBaseURIStatic() : gBaseURI;
            if (base.lastIndexOf("/") == (base.length - 1)) {
                base = base.substring(0, base.length - 1);
            }
            return base;
        } catch(ex) {
            this.logException(ex, "Error in tibco.admin.Main.getGBaseURI");
            throw new jsx3.lang.Exception("Exception thrown while retrieving gBaseURI", jsx3.NativeError.wrap(ex));
        }
    }

    /**
     * Internal function to get the base URI of the application when running from the filesystem.  Called by the
     * <code>getGBaseURI</code> function.
     *
     * @return {String} the gBaseURI
     * @throws {jsx3.lang.Exception} if an error occurs while calculating the static gBaseURI
     * @private
     * @jsxobf-clobber
     */
    main.getGBaseURIStatic = function() {
        try {
            if (!this.gBaseURIStatic) {
                var loc = window.location.toString();
                var index = loc.lastIndexOf("?");
                if (index > -1) { // strip off any parameters after the html file...
                    loc = loc.substring(0, index);
                }
                loc = loc.substring(0, loc.lastIndexOf("/"));
                // strip off the html file, etc
                var user = this.getServer().resolveURI("jsxuser://") + "JSXAPPS/";
                var index2 = user.indexOf("../");
                while (index2 == 0) {
                    user = user.substring(3);
                    loc = loc.substring(0, loc.lastIndexOf("/"));
                    // strip off the html file, etc
                    index2 = user.indexOf("../");
                }
                this.gBaseURIStatic = loc + "/" + user;
            }
            return this.gBaseURIStatic;
        } catch(ex) {
            this.logException(ex, "Error in tibco.admin.Main.getGBaseURIStatic");
            throw new jsx3.lang.Exception("Exception thrown while calculating the static gBaseURI", jsx3.NativeError.wrap(ex));
        }
    }

    /**
     * Utility function to determine if we are running the project from the filesystem or not.
     *
     * @return {boolean} <cdoe>true</code> if we are running off the filesystem, else <code>false</code>
     */
    main.isFileSystem = function() {
        return document.location.toString().indexOf("file:///") == 0;
    }

    /**
     * Given a plugin package's namespace, gets the contextRoot for that nemespace.
     *
     * @param namespace {String} the namespace of the plugin package
     *
     * @return {String} the contextRoot for the namespace
     *
     * @throws {tibco.admin.exception.InvalidNamespaceException} if the <code>namespace</code> argument is invalid
     * @throws {jsx3.lang.Exception} if an unknown error occurs while retrieving the contextRoot
     */
    main.getContextRootForNamespace = function(namespace) {
        if (!namespace) {
            throw new tibco.admin.exception.InvalidNamespaceException("The namespace is null or undefined");
        }
        if (typeof(namespace) != "string") {
            throw new tibco.admin.exception.InvalidNamespaceException("The namespace argument is not a string");
        }
        try {
            var ret = null;
            if (this.isFileSystem()) {
                ret = this.contextRoots[namespace];
                if (!ret) {
                    var index = namespace.lastIndexOf(".");
                    if (index > -1) {
                        ret = namespace.substring(index + 1);
                    }
                    else {
                        ret = namespace;
                    }
                    if (ret == "admin") {
                        ret = "administrator";
                    }
                }
            }
            else {
                ret = this.contextRoots[namespace];
                if(!ret) {
                    ret = adminContextRoot;
                }
                if (namespace == "tibco.admin") {
                    ret += "/JSXAPPS/administrator";
                }
            }
            return ret;
        } catch(ex) {
            this.logException(ex, "Error in tibco.admin.Main.getContextRootForNamespace");
            throw new jsx3.lang.Exception("Unknown exception while retrieving the context root for namespace " + namespace, jsx3.NativeError.wrap(ex));
        }
    }

    /**
     * Takes an input String (either XML or a URL) to a component.xml file, and returns a ComponentConfig object
     * from the data parsed in that xml file.
     *
     * @param str {String} A string of either the xml content of the component.xml file, or an absolute URL to
     * the component.xml file.
     *
     * @throws {tibco.admin.exception.InvalidComponentSchemaException} if the <code>component.xml</code> document is not valid
     * @throws {jsx3.lang.Exception} if an unknown error occurs while creating the {tibco.admin.util.component.ComponentConfig} object

     * @return {tibco.admin.util.component.ComponentConfig} the ComponentConfig representing that component's descriptor.
     */
    main.createConfig = function(str) {
        try {
            var id = tibco.admin.util.Performance.beginBenchmark("Creating Config for " + str);
            var ret = tibco.admin.util.component.ComponentUtils.createConfig(str);
            tibco.admin.util.Performance.endBenchmark(id);
            return ret;
        }
        catch(ex) {
            this.logException(ex, "Error in tibco.admin.Main.createConfig");
            throw jsx3.NativeError.wrap(ex);
        }
    }

    /**
     * Takes in interface name, and returns an object with all of the ComponentConfig objects for each plugin
     * component that implements that interface.  All the ComponentConfigs are indexed by their plugin implementation class
     * name, for instance:<br /><br />
     *
     *        <center><code>var pluginConfig = obj[impl_class_name];</code></center>
     *
     * <br />Will query the server to get the implementing plugins.  Since that is an asynchronous server call, a callback will
     * to give a response.  The callback will be done on the target and function specified in the input parameters <code>target</code>
     * and <code>fctn</code>.  Will return an object containing the ComponentConfig objects for all implementing plugin components,
     * indexed by the plugin components' plugin implementation class names.
     *
     * @throws {tibco.admin.exception.InvalidTargetException} if <code>target</code> is null or not an <code>Object</code>
     * @throws {tibco.admin.exception.InvalidFunctionException} if <code>fctn</code> is null, undefined, or not a function
     * @throws {jsx3.lang.Exception} if an unknown error occurs
     *
     * @param strInterface {String | jsx3.util.List} The fully-qualified name of the interface to find implementing plugin
     *                     components for, or a list of them.
     * @param target {jsx3.lang.Object} The target object whose function <code>fctn</code> should be called in the response.
     * @param fctn {function} The function to call on the <code>target</code> object when sending the response.
     * @param id {String} (optional) a unique identifier for the instance of the caller. Ids are automatically generated
     *                    to uniquely identify the calling class/function for service calls to a given rules file/operation,
     *                    and ignore responses from earlier (stale) requests.  This is not able identify unique <i>instances</i>
     *                    of the calling class, however.  If you may have multiple instance of the same class, this handling
     *                    of stale requests must be done on an <i>instance</i> basis.  This parameter allows you to specify
     *                    an id for the unique instance of the calling class.
     * @param bNoCancelOnNew {boolean} if <code>true</code>, multiple requests for the same rules file/operation from the same
     *                    calling location will all be used (responses from earlier requests will not be ignored).
     */
    main.getImplementingPlugins = function(strInterface, target, fctn, id, bNoCancelOnNew) {
        try {
            if (!target) {
                throw new tibco.admin.exception.InvalidTargetException("Specified 'target' object is null or undefined");
            }
            else if (!fctn) {
                throw new tibco.admin.exception.InvalidFunctionException("Specified function is null or undefined");
            }
            else if (typeof(target) != "object") {
                throw new tibco.admin.exception.InvalidTargetException("Specified 'target' is not an Object");
            }
            else if (typeof(fctn) != "function") {
                throw new tibco.admin.exception.InvalidFunctionException("Specified function is not a function");
            }
            //            else if(!jsx3.eval(target + "." + fctn)) {
            //                throw new tibco.admin.exception.InvalidFunctionException("The specified function is not a valid function on the target object");
            //            }
            var list = strInterface;
            if (!(strInterface instanceof jsx3.util.List)) {
                list = new jsx3.util.List(0);
                list.add(strInterface, 0);
            }
            //TODO capture exceptions from the following call once they are properly defined
            this.callgetImplementingPlugins(list, target, fctn, id, bNoCancelOnNew);
        }
        catch(ex) {
            this.logException(ex, "Error in tibco.admin.Main.getImplementingPlugins");
            if ((ex instanceof tibco.admin.exception.InvalidTargetException) || (ex instanceof tibco.admin.exception.InvalidFunctionException)) {
                throw ex;
            }
            else {
                throw new jsx3.lang.Exception("Unknown error in tibco.admin.Main.getImplementingPlugins()", jsx3.NativeError.wrap(ex));
            }
        }
    }

    /**
     * Internal function to get the {tibco.admin.util.component.ComponentConfig} for each of the plugins found during a call to <code>getImplementingPlugins()</code>,
     * and return them in the callback specifed by <code>target</code> and <code>fctn</code> in that same function.
     * <br /><br />
     * Two arguments will be passed to the callback function.<br />
     * <ul><li>The first argument will either be an associative array Object (with the
     * fully-qualified nameof the implementation class as the key, and the <code>tibco.admin.util.component.ComponentConfig</code> object as the value),
     * or a <code>tibco.admin.exception.ErrorLoadingPluginsException</code> if a fatal error occurred that prevented ANY
     * plugins from being loaded</li>
     * <li>The second argument will be a <code>tibco.admin.exception.CouldNotLoadPluginsException</code> if one or more plugins couldnot be loaded, but the
     * error was non-fatal,in that it did not prevent other plugins from loading.  This will be null or undefined if no exceptions occurred.</li></ul>
     *
     * @param interfaces {jsx3.util.List} A list whose elements are the names of the interfaces we were searching for implementing
     *                   plugin components of.
     * @param plugins {jsx3.util.List} A list of plugins that implement one the interfaces specified in <code>interfaces</code>.  Each of these
     *                is an Object with a {String} <code>contextRoot</code> field and a {jsx3.util.List} <code>components</code> field.  The {Object}
     *                elements of the <code>components</code> list will have a single {String} field called <code>path</code>, which will be the path to
     *                the component.xml file for a single implementing plugin component (relative to <code>contextRoot</code>).
     * @param target {Object} the target object on which to perform the callback to return the ComponentConfigs
     * @param fctn {function} the function of the target object <code>target</code> to be called in the callback to return the ComponentConfigs.
     * @private
     * @jsxobf-clobber
     */
    main.registerPlugins = function(interfaces, plugins, target, fctn) {
        try {
            var holder = new Object();
            var exceptions = new jsx3.util.List(0);
            if (plugins) {
                var iter = plugins.iterator();
                while (iter.hasNext()) {
                    try {
                        var plugin = iter.next();
                        var contextRoot = plugin.contextRoot;
                        //this.contextRoots[plugin.packageName]  = contextRoot;
                        //if (contextRoot == "amxadministrator") {
                        //    contextRoot += "/JSXAPPS/administrator";
                        //}
                        var components = plugin.components.iterator();
                        while (components.hasNext()) {
                            try {
                                var relPath = components.next().path;
                                if (!(relPath.lastIndexOf("component.xml") == (relPath.length - 13))) {
                                    relPath += (relPath.lastIndexOf("/") == (relPath.length - 1)) ? "component.xml" : "/component.xml";
                                }
                                try {
                                    var config = this.createConfig(tibco.admin.util.component.ComponentUtils.resolveURIFromContextRoot(contextRoot, relPath, true));
                                    config.setContextRoot(contextRoot);
                                    var cInterfaces = config.getPluginInterfaces();
                                    if (cInterfaces) {
                                        var iter2 = cInterfaces.getImplementedInterfaces();
                                        while (iter2.hasNext()) {
                                            try {
                                                var iObj = iter2.next().getName();
                                                if (interfaces.contains(iObj)) {
                                                    try {
                                                        this.registerPluginConfig(iObj, config, holder);
                                                    }
                                                    catch(ex1) {
                                                        exceptions.add(jsx3.NativeError.wrap(ex1), exceptions.size());
                                                    }
                                                }
                                            }
                                            catch(ex2) {
                                                exceptions.add(jsx3.NativeError.wrap(ex2), exceptions.size());
                                            }
                                        }
                                    }
                                }
                                catch(ex) {
                                    main.logException(ex);
                                    exceptions.add(new tibco.admin.exception.CouldNotLoadPluginException(this.resolveURIFromContextRoot(contextRoot, relPath), jsx3.NativeError.wrap(ex)), exceptions.size());
                                }
                            }
                            catch(ex3) {
                                exceptions.add(jsx3.NativeError.wrap(ex3), exceptions.size());
                            }
                        }
                    }
                    catch(ex4) {
                        exceptions.add(jsx3.NativeError.wrap(ex4), exceptions.size());
                    }
                }

            }
            var ret = holder;
            var except = null;
            if (exceptions.size() > 0) {
                except = new tibco.admin.exception.CouldNotLoadPluginsException(exceptions);
            }
            setTimeout(function() {
                fctn.call(target, ret, except);
            }, 0);
        }
        catch(ex5) {
            main.logException(ex5, "Error in tibco.admin.Main.registerPlugins");
            setTimeout(function() {
                fctn.call(target, new tibco.admin.exception.ErrorLoadingPluginsException(jsx3.NativeError.wrap(ex5)));
            }, 0);
        }
    }

    /**
     * Internal function to iterate the implemented interfaces in a ComponentConfig and find the ones that
     * implement the <code>strInterface</code> interface, and add them to the <code>holder</code> associative array, indexed by the
     * name of the implementation class.
     *
     * @param strInterface {String} the name of the interface to find implementation classes of
     * @param config {tibco.admin.util.component.ComponentConfig} the ComponentConfig to search for implementation
     *               classes
     * @param holder {Object} the associative array object to add the ComponentConfig to, indexed by the
     *               implementation class name
     * @private
     * @jsxobf-clobber
     */
    main.registerPluginConfig = function(strInterface, config, holder) {
        try {
            var interfaces = config.getPluginInterfaces().getImplementedInterfaces();
            while (interfaces.hasNext()) {
                try {
                    var interfaceInst = interfaces.next();
                    if (interfaceInst.getName() == strInterface) {
                        holder[interfaceInst.getImplementationClass()] = config;
                    }
                } catch(ex1) {
                    tibco.admin.Main.logException(ex1);
                }
            }
        } catch(ex) {
            this.logException(ex, "Error in tibco.admin.Main.registerPluginConfig");
            throw new tibco.admin.exception.CouldNotRegisterPluginsException(strInterface, jsx3.NativeError.wrap(ex));
        }
    }

    /**
     * Loads an external resource into this server instance. What this method does depends on the <code>strType</code>
     * parameter.
     * <ul>
     *   <li><code>css</code> - Loads a CSS file asynchronously into the memory space of the page hosting this
     *       application; returns <code>null</code>.</li>
     *   <li><code>xml</code> or <code>xsl</code> - Loads an XML file synchronously into the XML cache of this
     *       application; <code>strId</code> is ignored; returns the loaded {jsx3.xml.Document} instance.</li>
     *   <li><code>jss</code> or <code>ljss</code> - Loads a dynamic properties file or localized properties bundle
     *       synchronously into this application; <code>strId</code> is ignored; returns <code>null</code>.</li>
     * </ul>
     *
     * @param strSrc {String} The path to the resource, relative to the context root of its owning component.
     * @param namespace {String | tibco.admin.util.component.ComponentConfig} the namespace of the plugin package
     *                  that the resource belongs to.
     * @param strId {String} the unique identifier of the resource. A resource loaded by this method may clobber
     *              a previously loaded resource of the same type and id.  This will be the id of the resource in the cache.
     * @param strType {String} the type of include, one of: <code>css</code>, <code>jss</code>, <code>xml</code>,
     *                <code>xsl</code>, or <code>ljss</code>.
     * @param bReload {boolean} if <code>true</code>, a CSS file is reloaded from the remote server without checking
     *                the local browser cache. Other types of resources are not affected by this parameter.
     * @return {jsx3.xml.Document | null} the return type depends on the <code>strType</code>
     *          parameter. See the method description.
     *
     * @throws {jsx3.lang.Exception} if an error occurs while loading the resource.
     */
    main.loadResource = function(strSrc, namespace, strId, strType, bReload) {
        try {
            if (!(strType == "css") && !(strType == "jss") && !(strType == "ljss") && !(strType == "xml") && !(strType == "xsl")) {
                throw new tibco.admin.exception.UnsupportedResourceTypeException(strType);
            }
            var id = tibco.admin.util.Performance.beginBenchmark("Loading resource " + strSrc);
            var absURI = this.resolveURI(namespace, strSrc, true);
            var ret = null;
            try {
                ret = this.getServer().loadInclude(absURI, strId, strType, bReload);
            }
            catch(failed) {
                try {
                    tibco.admin.Main.getServer().getCache().clearById(strId ? strId : absURI);
                }
                catch(innerfail) {
                    // do nothing
                }
            }
            tibco.admin.util.Performance.endBenchmark(id);
            return ret;
        } catch(ex) {
            this.logException(ex, "Error in tibco.admin.Main.loadResource");
            if (main._sessionExpired())
                return;
            if (ex instanceof tibco.admin.exception.UnsupportedResourceTypeException) {
                throw ex;
            }
            else {
                throw new jsx3.lang.Exception("Could not load resource " + strSrc, jsx3.NativeError.wrap(ex));
            }
        }
    }

    /**
     * Loads one or more external resources specified in an external xml document into this server instance.
     * What this method does depends on various attributes defiend in xml document located at <code>strPath</code>  parameter
     *
     * <h3>Attribute namespace {String}:</h3>
     * <ul>
     *   <li>the namespace of the plugin packag that the resource belongs to. Will use parameter <code>namespace</code> as default</li>
     * </ul>
     * <h3>Attribute id {String}:</h3>
     * <ul>
     *   <li>the unique identifier of the resource. A resource loaded by this method may clobber
     *       a previously loaded resource of the same type and id.  This will be the id of the resource in the cache.
     *       Will use parameter <code>strPath</code> as default </li>
     * </ul>
     * <h3>Attribute type:</h3>
     * <h3>Attribute type:</h3>
     * <ul>
     *   <li><code>script</code> - Given the absolute path to the .js file, or the relative path and it's plugin component's
     *       namespace, retrieves the .js file from the server, and loads the class.</li>
     *   <li><code>css</code> - Loads a CSS file asynchronously into the memory space of the page hosting this
     *       application.</li>
     *   <li><code>xml</code> or <code>xsl</code> - Loads an XML file synchronously into the XML cache of this
     *       application.</li>
     *   <li><code>jss</code> or <code>ljss</code> - Loads a dynamic properties file or localized properties bundle
     *       synchronously into this application.</li>
     * </ul>
     * <h3>Sample of a resource.xml file:</h3>
     *
     * <code>
     * &lt;data jsxid=&quot;jsxroot&quot;&gt;<br />
     * &nbsp;&nbsp;&nbsp;&nbsp;&lt;record jsxid=&quot;1&quot; path=&quot;tibco/admin/enterpriseAssets/envDef/envDtls/genInfo/jss/genInfo_en.xml&quot; id=&quot;&quot; type=&quot;jss&quot; namespace=&quot;tibco.admin&quot;/&gt;<br />
     * &nbsp;&nbsp;&nbsp;&nbsp;&lt;record jsxid=&quot;2&quot; path=&quot;tibco/admin/enterpriseAssets/envDef/envDtls/genInfo/GenInfo.js&quot; type=&quot;script&quot;  /&gt;<br />
     * &nbsp;&nbsp;&nbsp;&nbsp;&lt;record jsxid=&quot;2&quot; path=&quot;tibco/admin/enterpriseAssets/envDef/envDtls/genInfo/GenInfoAdd.js&quot; type=&quot;script&quot;  /&gt;<br />
     * &lt;/data&gt;
     * </code>
     *
     * @param strPath {String} The path to the xml document, relative to the context root of its owning component.
     * @param namespace {String | tibco.admin.util.component.ComponentConfig} the namespace of the plugin package that the resource belongs to.
     *
     * @throws {jsx3.lang.IllegalArgumentException} if the caller of this function does not pass arguments according to the method's contract.
     *
     * @throws {tibco.admin.exception.InvalidXMLDocException} if xml document located in <code>strPath</code> is invalid or not found.
     *
     * @throws {jsx3.lang.Exception} if an error occurs while loading one of the the resources specified in <code>strPath</code..
     */
    main.loadResources = function(strPath, namespace) {
        var absURI, resDocument, nodeList, next, path, strId, type ;

        try {
            absURI = main.resolveURI(namespace, strPath)
        }
        catch(e) {
            throw new jsx3.lang.IllegalArgumentException("\nstrPath = " + strPath + "\nnamespace = " + namespace, jsx3.NativeError.wrap(e));
        }
        try {
            resDocument = jsx3.xml.CDF.Document.newDocument();
            resDocument.setAsync(false);
            resDocument.setValidateOnParse(true)
            resDocument = resDocument.load(absURI);
            if (resDocument.getError().code != 0) {
                throw  resDocument
            }
            nodeList = resDocument.selectNodes("//record");
        }
        catch(e) {
            if (main._sessionExpired())
                return;
            throw new tibco.admin.exception.InvalidXMLDocException("\nPlease check if " + absURI + " exists and is a valid document.", jsx3.NativeError.wrap(e));
        }
        try {
            var strNamespace = namespace ;
            for (i = 0; i < nodeList.size(); i++) {
                next = nodeList.get(i);
                path = next.getAttribute("path");
                type = next.getAttribute("type");
                strNamespace = next.getAttribute("namespace")
                if (!strNamespace || strNamespace == "") {
                    strNamespace = namespace
                }
                if (next.getAttribute("type") == "script") {
                    try {
                        main.loadClass(path, strNamespace)
                    }
                    catch(e) {
                        throw e;
                    }
                }
                if (type == "jss" || type == "ljss" || type == "xml" || type == "xsl" || type == "css") {
                    strId = (strId = next.getAttribute("strId")) ? strId : path;
                    main.getServer().loadInclude(main.resolveURI(strNamespace, path), strId, type, false);
                }
            }
        }
        catch(e) {
            if (main._sessionExpired())
                return;
            throw new jsx3.lang.Exception("Could not load resource .\nCan't load one or more resources located in document  " + absURI, jsx3.NativeError.wrap(e));
        }
    }

    /**
     * Given the absolute path to the .js file, or the relative path and it's plugin component's
     * namespace, retrieves the .js file from the server, and loads the class.
     *
     * @param path {String} either an absolute URL to the .js file (<code>namespace</code> MUST not be
     *            specified), or a relative path to the .js file (<code>namespace</code> MUST be specified, and must be
     *            the plugin package namespace for the class' plugin).
     * @param namespace {String | tibco.admin.util.component.ComponentConfig} the plugin package namespace for the class'
     *                  plugin package, or the <code>ComponentConfig</code> for the component that owns this class.
     *                  Used to resolve the path to the .js file, given the class name specified in <code>path</code>.
     *                  (Must be <code>null</code> if <code>path</code> is an absoulte url to the .js file).
     *
     * @throws {tibco.admin.exception.LoadClassFailedException} if an error occurs while loading the class.
     */
    main.loadClass = function(path, namespace) {
        try {
            var id = tibco.admin.util.Performance.beginBenchmark("Loading class " + path);
            if (path.indexOf("://") < 0) {
                if (namespace) {
                    var fullpath = this.resolveURI(namespace, path, true);
                    this._loadClass(fullpath);
                }
                else {
                    throw new tibco.admin.exception.InvalidURIException(path + " is an invalid URI.  An absoulte path was expected because the namespace argument was null or undefined");
                }
            }
            else {
                this._loadClass(path);
            }
            tibco.admin.util.Performance.endBenchmark(id);
        } catch(ex) {
            this.logException(ex, "Error in tibco.admin.Main.loadClass");
            throw new tibco.admin.exception.LoadClassFailedException("Load Class failed for path " + path + " and namespace " + namespace, jsx3.NativeError.wrap(ex));
        }
    }

    /**
     * Given the name of a class to be loaded, looks up the plugin implementation class in the given ComponentConfig
     * object, retrieves the .js file from the server, and loads the class.<br /><br />
     *
     * @param className {String} The fully-qualified name of the class to be loaded.
     * @param config {tibco.admin.util.component.ComponentConfig} the ComponentConfig for the plugin component whose
     *                  plugin implementation class is to be loaded. Used to resolve the path to the .js file, given the
     *                  specified <code>className</code>.
     *
     * @throws {tibco.admin.exception.LoadClassFailedException} if an error occurs while loading the class.
     */
    main.loadClassByName = function(className, config) {
        try {
            var id = tibco.admin.util.Performance.beginBenchmark("Loading class with name: " + className);
            if (config) {
                if (config instanceof tibco.admin.util.component.ComponentConfig) {
                    if (!jsx3.lang.Class.forName(className)) {
                        var path = tibco.admin.util.component.ComponentUtils.resolveURIFromContextRoot(config.getContextRoot(), config.getPathToClass(className));
                        this._loadClass(path);
                    }
                }
                else {
                    // throw an exception?
                }
            }
            else {
                // throw an exception?
            }
            tibco.admin.util.Performance.endBenchmark(id);
        } catch(ex) {
            this.logException(ex, "Error in tibco.admin.Main.loadClassByName");
            throw new tibco.admin.exception.LoadClassFailedException("Load Class failed for " + className, jsx3.NativeError.wrap(ex));
        }
    }

    /**
     * Given a class name, and a map object containing the local variable stack context, retrieves an instance
     * of a plugin implementation class.
     *
     * @param className {String} the fully-qualified name of the plugin implementation class to get an instance of
     * @param args {Object} a map containing the local variable stack context
     *
     * @throws {jsx3.lang.Exception} if an error occurs while trying to get an instance of the plugin class
     *
     * @return {Object} an instance of the class with name pluginClass.  Uses the <code>getInstance()</code> method of the
     *          tibco.admin.plugin.PluginInterface interface.
     */
    main.getPluginClassInstance = function(className, args) {
        var myArgs = new Object();
        myArgs["args"] = args;
        try {
            return jsx3.eval(className + ".getInstance(args);", myArgs);
        }
        catch(ex) {
            this.logException(ex, "Error in tibco.admin.Main.getPluginClassInstance");
            throw new jsx3.lang.Exception("Error getting instance of class " + className, jsx3.NativeError.wrap(ex));
        }
    }

    /**
     * Given the absolute URL to a class' .js file, retrieves the .js file from the server, and loads the class
     * into the browser's memory for the current page.
     *
     * @param absPath {String} the absolute URL to the class to be loaded.
     *
     * @throws {tibco.admin.exception.LoadClassFailedException} if an error occurs while loading the class.
     *
     * @private
     * @jsxobj-clobber
     */
    main._loadClass = function(absPath) {
        try {
            var req = new jsx3.net.Request();
            req.open("GET", absPath, false);
            var myreq = req.send();
            var content = req.getResponseText();
            if (req.getStatus() != 200) {
                throw "Status is " + req.getStatus();
            }
            else if (!content) {
                throw "ResponseText is null";
            }
            else if (content.indexOf("Matrix_Administrator_Login_Test_For_SessionTimeOut") != -1) {
                throw "Matrix_Administrator_Login_Test_For_SessionTimeOut is loaded. Not a valid Java Script";
            }
            jsx3.eval(content);
        } catch(ex) {
            this.logException(ex, "Error in tibco.admin.Main._loadClass");
            if (main._sessionExpired())
                return;
            throw new tibco.admin.exception.LoadClassFailedException("Load Class failed class at absolute path " + absPath, jsx3.NativeError.wrap(ex));
        }
    }

    /**
     * Loads a particular serialized UI resource into a parent block, automatically resolving URLs within the
     * Admin UI plugin framework.
     *
     * @param parent {jsx3.app.Model} the Model object representing the parent object to load the resource as
     *               a child of.
     * @param namespace {String | tibco.admin.util.component.ComponentConfig} the namespace of the resource's plugin
     *                  package, or the <code>ComponentConfig</code> for the component that owns this UI Resource
     * @param resourcePath {String} the path (relative to the component path of the plugin component) of the
     *                     resource to be loaded into <code>parent</code>
     * @param repaint {boolean} flag to indicate whether or not to repaint the parent after the child is loaded. Default
     *                is <code>false</code> (no repaint)
     *
     * @throws {jsx3.lang.Exception} if an error occurs while loading the resource
     *
     * @return {jsx3.app.Model} The deserialized object that was loaded into <code>parent</code>
     */
    main.loadUIResource = function(parent, namespace, resourcePath, repaint) {
        tibco.admin.util.Log.SYSTEM.info("tibco.admin.Main#loadUIResource: resourcePath = " + resourcePath)
        try {
            var absPath = this.resolveURI(namespace, resourcePath);
            var objXML = this.getServer().getCache().getDocument(absPath)
            if (objXML == null) {
                var req = new jsx3.net.Request();
                if (tibco.admin.Main.isFileSystem()) {
                    req.open("GET", absPath, false);
                }
                else {
                    var contextroot = this.contextRoots[namespace]
                    contextrootfull = "/" + contextroot + "/"
                    var strpath = "/amxadministrator/uiresourcecheck?file=" + absPath.replace(contextrootfull, "") + "&contextroot=" + contextroot + "&ts=" + new Date()
                    tibco.admin.util.Log.SYSTEM.info("tibco.admin.Main#loadUIResource Runinig at server.\npath = " + strpath)
                    req.open("GET", strpath, false);
                }
                req.send();
                objXML = req.getResponseXML()
                tibco.admin.util.Log.SYSTEM.info("tibco.admin.Main#loadUIResource: HTTP response code  = " + req.getStatus())
                if (req.getStatus() != 200) {
                    if (main._sessionExpired()){
                        return null;
                    }
                    if(req.getStatus() == 500){
                        tibco.admin.Main.getServer().alert("500",req.getResponseText(),null,"Ok",{width:600,height:400})
                    }
                }
                main.getServer().getCache().setDocument(absPath, objXML);
            }
            return parent.loadXML(objXML, repaint);
        } catch(ex) {
            this.logException(ex, "Error in tibco.admin.Main.loadUIResource");
            if (main._sessionExpired())
                return;
            throw new jsx3.lang.Exception("Error loading UI Resource " + resourcePath + " with namespace " + namespace, jsx3.NativeError.wrap(ex));
        }
    }

   /**
     * Pre loads a particular serialized UI resource into cache, automatically resolving URLs within the
     * Admin UI plugin framework. Is used for ACL to test if user has access right to the UI
     *
     * @param namespace {String | tibco.admin.util.component.ComponentConfig} the namespace of the resource's plugin
     *                  package, or the <code>ComponentConfig</code> for the component that owns this UI Resource
     * @param resourcePath {String} the path (relative to the component path of the plugin component) of the
     *                     resource to be loaded into <code>parent</code>
     * @throws {jsx3.lang.Exception} if an error occurs while loading the resource
     *
     * @private
     *
     * @return {int} HTTP response code (i.e., 200 OK , 404 Not Found, 500 Internal Server Error, 403 Forbidden etc);
     */
    main.preLoadUIResource = function(namespace, resourcePath) {
        tibco.admin.util.Log.SYSTEM.info("tibco.admin.Main#preLoadUIResource: resourcePath = " + resourcePath)
        try {
            var loadedstatus = 0;
            if (namespace == "tibco.admin") {
                var prefix = "JSXAPPS/administrator/"
                resourcePath = resourcePath.replace(prefix, "")
            }
            var absPath = this.resolveURI(namespace, resourcePath);
            var req = new jsx3.net.Request();
            if (tibco.admin.Main.isFileSystem()) {
                req.open("GET", absPath, false);
            }
            else {
                var contextroot = this.contextRoots[namespace]
                contextrootfull = "/" + contextroot + "/"
                //alert("namespace " + namespace +" contextroot  "+contextroot)
                var strpath = "/amxadministrator/uiresourcecheck?file=" + absPath.replace(contextrootfull, "") + "&contextroot=" + contextroot + "&ts=" + new Date()
                tibco.admin.util.Log.SYSTEM.info("tibco.admin.Main#preLoadUIResource Runinig at server.\npath = " + strpath)
                req.open("GET", strpath, false);
            }
            req.send();
            objXML = req.getResponseXML()
            tibco.admin.util.Log.SYSTEM.info("tibco.admin.Main#preLoadUIResource: HTTP response code  = " + req.getStatus())
            loadedstatus = req.getStatus()
            if (loadedstatus == 200) {
                main.getServer().getCache().setDocument(absPath, objXML);
            }
            else {
                if (main._sessionExpired()) {
                    return null;
                }
                if (loadedstatus == 500) {
                    tibco.admin.Main.getServer().alert("500", req.getResponseText(),null,"Ok",{width:600,height:400})
                }
            }
            return loadedstatus

        } catch(ex) {
            this.logException(ex, "Error in tibco.admin.Main.loadUIResource");
            if (main._sessionExpired())
                return;
            throw new jsx3.lang.Exception("Error loading UI Resource " + resourcePath + " with namespace " + namespace, jsx3.NativeError.wrap(ex));
        }
    }


    /**
     * Asynchronously loads a particular serialized UI resource into a parent block, automatically resolving URLs within the
     * Admin UI plugin framework. Notifies the caller of the completed load on the <code>callbackTarget</code>'s
     * <code>callbackFunction</code> function.  The callback will pass an object with a <code>resourcePath</code> field, which will
     * have the original <code>resourcePath</code> specified here, a <code>namespace</code> field conaining the original
     * <code>namespace</code> specified here, a <code>status</code> field with a String value of either
     * <code>SUCCESS</code> or <code>FAILURE</code>, a <code>message</code> field containing any error message, an <code>exception</code>
     * field, containing any {jsx3.lang.Exception} thrown, and an <code>obj</code> field containing the
     * deserialized {jsx3.app.Model} object that was loaded.
     *
     * If you need asynchronous loading of components, but chanied to guarantee the order in which they are loaded, load
     * successive components from the callbackFunction.  This synchronizes the loading of multiple UI files, but allows
     * the rest of the application to continue to work (repainting, etc). rather than waiting for the UI objects to load before
     * coninuing.
     *
     * @param parent {jsx3.app.Model} the Model object representing the parent object to load the resource as
     *               a child of.
     * @param namespace {String | tibco.admin.util.component.ComponentConfig} the namespace of the resource's plugin package
     *                  or the <code>ComponentConfig</code> for the component that owns this UI Resource
     * @param resourcePath {String} the path (relative to the component path of the plugin component) of the
     *                     resource to be loaded into <code>parent</code>
     * @param repaint {boolean} flag to indicate whether or not to repaint the parent after the child is loaded. Default
     *                is <code>false</code> (no repaint)
     * @param callbackTarget {Object} the target object on which the callback to <code>callbackFunction</code> should be made
     *                       when the load operation is complete.
     * @param callbackFunction {Function} the function to call on <code>callbackTarget</code> to perform the callback once
     *                         the load operation is complete.
     *
     * @throws {tibco.admin.exception.InvalidTargetException} if <code>callbackTarget</code> and <code>callbackFunction</code>
     *         are specified, but the <code>callbackTarget</code> is not an Object
     * @throws {tibco.admin.exception.InvalidFunctionException} if <code>callbackTarget</code> and <code>callbackFunction</code>
     *         are specified, but <code>callbackFunction</code> is not a function
     * @throws {jsx3.lang.Exception} if an unknown error occurs
     */
    main.loadUIResourceAsync = function(parent, namespace, resourcePath, callbackTarget, callbackFunction, repaint) {
        tibco.admin.util.Log.SYSTEM.info("tibco.admin.Main#loadUIResourceAsync: resourcePath = " + resourcePath)
        try {
            if (!parent) {
                throw new jsx3.lang.Exception("Parent is not defnied.  Cannot load UI Resource into non-existent parent");
            }
            if (callbackTarget && callbackFunction) {
                if (typeof(callbackTarget) != "object") {
                    throw new tibco.admin.exception.InvalidTargetException("Specified callback target is not an Object");
                }
                else if (typeof(callbackFunction) != "function") {
                    throw new tibco.admin.exception.InvalidFunctionException("Specified callback function is not a function");
                }
                //                else if(!jsx3.eval(callbackTarget + "." + callbackFunction)) {
                //                    throw new tibco.admin.exception.InvalidFunctionException("The specified callback function is not a valid function on the callback target object")
                //                }
            }
            var absPath = this.resolveURI(namespace, resourcePath);
            var objXML = this.getServer().getCache().getDocument(absPath)
            if (objXML != null) {
                jsx3.sleep(function() {
                    tibco.admin.util.Log.SYSTEM.info("loadUIResourceAsync loading from cache ")
                    var msg = new Object();
                    msg.obj = parent.loadXML(objXML, true);
                    msg.resourcePath = resourcePath;
                    msg.namespace = namespace;
                    msg.headers = null;
                    msg.exception = null;
                    msg.status = "SUCCESS";
                    msg.message = "Successfully loaded UI Resource " + resourcePath + " from cache";
                    if (callbackTarget && callbackFunction) {
                        setTimeout(function() {
                            callbackFunction.call(callbackTarget, msg);
                        }, 0);
                    }
                }, null, main);
            }
            else {
                tibco.admin.util.Log.SYSTEM.info("loadUIResourceAsync loading from servlet ")
                var req = new jsx3.net.Request();
                req.parent = parent;
                req.repaint = repaint;
                req.resourcePath = resourcePath;
                req.namespace = namespace;
                req.callbackTarget = callbackTarget;
                req.callbackFunction = callbackFunction;
                req.absPath = absPath ;
                if (tibco.admin.Main.isFileSystem()) {
                    req.open("GET", absPath, true);
                }
                else {
                    var contextroot = this.contextRoots[namespace]
                    tibco.admin.util.Log.SYSTEM.info("contextroot = " + contextroot)
                    tibco.admin.util.Log.SYSTEM.info("absPath = " + absPath)
                    contextrootfull = "/" + contextroot + "/"
                    var strpath = "/amxadministrator/uiresourcecheck?file=" + absPath.replace(contextrootfull, "") + "&contextroot=" + contextroot + "&ts=" + new Date()
                    tibco.admin.util.Log.SYSTEM.info("tibco.admin.Main#loadUIResourceAsync Runinig at server.\npath = " + strpath)
                    req.open("GET", strpath, true);
                    tibco.admin.util.Log.SYSTEM.info("tibco.admin.Main#loadUIResourceAsync invoked open");
                }
                req.subscribe(jsx3.net.Request.EVENT_ON_RESPONSE, main.onLoadUIResourceResponse);
                req.subscribe(jsx3.net.Request.EVENT_ON_TIMEOUT, main.onLoadUIResourceTimeout);
                req.send(null, 10000);
            }
        } catch(ex) {
            this.logException(ex, "Error in tibco.admin.Main.loadUIResourceAsync")
            if ((ex instanceof tibco.admin.exception.InvalidTargetException) || (ex instanceof tibco.admin.exception.InvalidFunctionException)) {
                throw ex;
            }
            else {
                throw new jsx3.lang.Exception("Unknown error occurred while trying to asynchronously load UI Resource " + resourcePath + " with namespace " + namespace, jsx3.NativeError.wrap(ex));
            }
        }
    }

    /**
    * Given a CDF cache document and record id, shows an error dialog by simply providing the attributes of that row to use
    * as the summary and details of the dialog.
    *
    * @param cacheId {String} the id of the CDF document in the cache
    * @param jsxid {String} the jsxid of the record to be used in the document identified by <code>cacheId</code>
    * @param shortMessageAtt {String} the name of the attribute in the record to use as the content of the summary
    * @param longMessageAtt {String} the name of the attribute in the record to use as the content of the details
    * @param shortLabel {String} the optional label to use for the summary area
    * @param longLabel {String} the optional label to use for the details area
    * @param dialogTitle {String} the optional title for the dialog
    */
    main.showErrorDialog = function(cacheId, jsxid, shortMessageAtt, longMessageAtt, shortLabel, longLabel, dialogTitle) {
        var doc = this.getServer().getCache().getDocument(cacheId);
        if (doc) {
            var record = doc.getRecordNode(jsxid);
            if (record) {
                var shortMessage = record.getAttribute(shortMessageAtt);
                var longMessage = record.getAttribute(longMessageAtt);
                if (!shortLabel) {
                    shortLabel = this.getServer().getDynamicProperty("@ErrMsgDialog Summary Label");
                }
                if (!longLabel) {
                    longLabel = this.getServer().getDynamicProperty("@ErrMsgDialog Details Label");
                }
                if (!dialogTitle) {
                    dialogTitle = this.getServer().getDynamicProperty("@ErrMsgDialog Title");
                }
                var block = this.getServer().getJSXByName("dlgMessageDetails_30");
                if (!block) {
                    var bodyblock = this.getServer().getBodyBlock();
                    block = bodyblock.load("jsxaddin://user!MatrixAdmin/components/messageDialog.xml", true);
                }
                block.getDescendantOfName("txtMessageSummary_30").setValue(shortMessage, true);
                block.getDescendantOfName("txtbxMessageDetails_30").setValue(longMessage, true);
                block.getDescendantOfName("lblMessageSummaryLabel_30").setText(shortLabel, true);
                block.getDescendantOfName("lblMessageDetailsLabel_30").setText(longLabel, true);
                block.getDescendantOfName("windowBar").setText(dialogTitle, true);
            }
            else {
                // throw exception?
            }
        }
        else {
            // throw exception here?
        }
    }

    /**
     * Private function to handle a response from a <code>loadUIResource()</code> call.
     *
     * @param objEvent {Object} the response message object from the subscription callback
     *
     * @private
     * @jsxobf-clobber
     */
    main.onLoadUIResourceResponse = function(objEvent) {
        tibco.admin.util.Log.SYSTEM.info("tibco.admin.Main#onLoadUIResourceResponse Call back function for async loading objEvent " + objEvent)
        try {
            var req = objEvent.target;
            tibco.admin.util.Log.SYSTEM.info("tibco.admin.Main#onLoadUIResourceResponse. status for request " + req + "is "+req.getStatus())
            var msg = new Object();
            msg.resourcePath = req.resourcePath;
            msg.namespace = req.namespace;
            msg.headers = req.getAllResponseHeaders();
            if (req.getStatus() == "200") {
                msg.exception = null;
                msg.status = "SUCCESS";
                msg.message = "Successfully loaded UI Resource " + req.resourcePath;
                try {
                    msg.obj = req.parent.loadXML(req.getResponseXML(), true);
                    try {
                        main.getServer().getCache().setDocument(req.absPath , req.getResponseXML());
                    } catch(failed) {
                        try {
                            tibco.admin.Main.getServer().getCache().clearById(req.absPath);
                        }
                        catch(innerfail) {
                            // do nothing
                        }
                    }
                } catch (ex) {
                    if (main._sessionExpired())
                        return;
                    msg.status = "FAILURE";
                    msg.exception = jsx3.NativeError.wrap(ex);
                    msg.message = "Successfully retrieved UI Resource " + req.resourcePath + " but failed to load it into the parent object\nException thrown: " + msg.exception.getMessage();
                    main.logException(ex);
                }
            }
            else {
                if (main._sessionExpired())
                    return;
                msg.exception = null;
                msg.status = "FAILURE";
                msg.obj = null;
                msg.message = "Failed to load UI Resource " + req.resourcePath + "\nHTTP Response Code: " + req.getStatus() + "\nHTTP Response Text: " + req.getStatusText();
                if (req.getStatus() == 500) {
                    tibco.admin.Main.getServer().alert("500", req.getResponseText(),null,"Ok",{width:600,height:400})
                }
            }
            if (req.callbackTarget && req.callbackFunction) {
                setTimeout(function() {
                    req.callbackFunction.call(req.callbackTarget, msg);
                }, 0);
            }
        } catch(ex) {
            tibco.admin.util.Log.SYSTEM.error("Error in tibco.admin.Main.onLoadUIResourceResponse 0" + ex)
            if (main._sessionExpired())
                return;
            main.logException(ex, "Error in tibco.admin.Main.onLoadUIResourceResponse")
            if (req.callbackTarget && req.callbackFunction) {
                msg.exception = ex;
                msg.status = "FAILURE";
                msg.message = "Failed to load UI Resource " + req.resourcePath;
                if (req.callbackTarget && req.callbackFunction) {
                    setTimeout(function() {
                        req.callbackFunction.call(req.callbackTarget, msg);
                    }, 0);
                }
            }
            if (objEvent.target.getStatus() == 500) {
               tibco.admin.Main.getServer().alert("500", objEvent.target.getResponseText(),null,"Ok",{width:600,height:400})
            }
        }
    }

    /**
     * Private function to handle a timeout on a <code>loadUIResource()</code> call.
     *
     * @param objEvent {Object} the response message object from the subscription callback
     *
     * @private
     * @jsxobf-clobber
     */
    main.onLoadUIResourceTimeout = function(objEvent) {
        try {
            if (main._sessionExpired())
                return;
            var req = objEvent.target;
            var msg = new Object();
            msg.resourcePath = req.resourcePath;
            msg.namespace = req.namespace;
            msg.headers = req.getAllResponseHeaders();
            msg.exception = null;
            msg.status = "FAILURE";
            msg.obj = null;
            msg.message = "Failed to load UI Resource " + req.resourcePath + "\nOperation Timed Out";
            if (objEvent.target.getStatus() == 500) {
               tibco.admin.Main.getServer().alert("500", objEvent.target.getResponseText(),null,"Ok",{width:600,height:400})
            }
            if (req.callbackTarget && req.callbackFunction) {
                setTimeout(function() {
                    req.callbackFunction.call(req.callbackTarget, msg);
                }, 0);
            }
        } catch(ex) {
            main.logException(ex, "Error in tibco.admin.Main.onLoadUIResourceTimeout")
            if (main._sessionExpired())
                return;
            if (req.callbackTarget && req.callbackFunction) {
                msg.exception = ex;
                msg.status = "FAILURE";
                if (req.callbackTarget && req.callbackFunction) {
                    setTimeout(function() {
                        req.callbackFunction.call(req.callbackTarget, msg);
                    }, 0);
                }
            }
        }
    }

    /**
     * Internal function. Performs a service call to get all implementing plugin components for some given interfaces.
     *
     * @param list {jsx3.util.List} a list of the fully-qualified names of the interfaces to find implementing
     *             plugin components for.
     * @param target {Object} the target object on which to perform the response callback
     * @param fctn {function} the function to call on <code>target</code> to perform the response callback
     *
     * @private
     * @jsxobf-clobber
     */
    main.callgetImplementingPlugins = function(list, target, fctn, id, bNoCancelOnNew) {
        try {
            jsx3.require("jsx3.net.Service");
            var objService = this.loadService("tibco.admin", "rules/getImplementingPlugins.xml", "getPlugins", null, id, bNoCancelOnNew);
            if (this.getServer().getEnv("MODE")) {
                objService.setEndpointURL(tibco.admin.util.ServiceHelper.getPluginsEndpointURL());
            }
            else {
                // mode is static
                jsx3.log(" env mode " + this.getServer().getEnv("MODE"))
                objService.setInboundURL(tibco.admin.Main.resolveURI("tibco.admin", 'docs/' + list.get(0) + '.xml'));
            }
            objService.interfaceList = list;
            objService.interfaces = list.iterator();
            objService.callbackTarget = target;
            objService.callbackFunction = fctn;
            //subscribe and call
            objService.subscribe(jsx3.net.Service.ON_SUCCESS, main.ongetImplementingPluginsSuccess);
            objService.subscribe(jsx3.net.Service.ON_ERROR, main.ongetImplementingPluginsError);
            objService.subscribe(jsx3.net.Service.ON_INVALID, main.ongetImplementingPluginsInvalid);
            objService.doCall();
        } catch(ex) {
            main.logException(ex, "Error in tibco.admin.Main.callgetImplementingPlugins");
            throw new jsx3.lang.Exception("Error getting implementing plugins", jsx3.NativeError.wrap(ex));
        }
    };

    /**
     * Internal function. Called when a successful service call has been made to get implementing plugin components
     * for a given list of interfaces. Calls register plugins, which then notifies the requesting plugin container
     * of the plugins via a callback.
     *
     * @param objEvent {Object} the response object from the service call.
     * @private
     * @jsxobf-clobber
     */
    main.ongetImplementingPluginsSuccess = function(objEvent) {
        try {
            //        tibco.admin.util.PendingOperationsHandler.removeOperation(objEvent.target.opId);
            tibco.admin.util.ServiceHelper.printInboundOutboundDocument(objEvent);
            main.registerPlugins(objEvent.target.interfaceList, objEvent.target.plugins, objEvent.target.callbackTarget, objEvent.target.callbackFunction);
        } catch(ex) {
            main.logException(ex, "Error in tibco.admin.Main.ongetImplementingPluginsSuccess")
            setTimeout(function() {
                objEvent.target.callbackFunction.call(objEvent.target.callbackTarget, ex);
            }, 0);
        }

        //        tibco.admin.util.ServiceHelper.logErrors('responseAddEnvironment399CDF', 'AddEnvironment', 'addEnvironment', objEvent);
    };

    /**
     * Internal function. Called when an error occurs in the service call to get implementing plugin components
     * for a given list of interfaces. Notifies the requesting plugin container of the error by returning <code>null</code>
     * in the callback.
     *
     * @param objEvent {Object} the response object from the service call.
     * @private
     * @jsxobf-clobber
     */
    main.ongetImplementingPluginsError = function(objEvent) {
        try {
            //        tibco.admin.util.PendingOperationsHandler.removeOperation(objEvent.target.opId);
            tibco.admin.util.ServiceHelper.printInboundOutboundDocument(objEvent);
            //        tibco.admin.util.ServiceHelper.logErrors('responseAddEnvironment399CDF', 'AddEnvironment', 'addEnvironment', objEvent);
            setTimeout(function() {
                objEvent.target.callbackFunction.call(objEvent.target.callbackTarget, null);
            }, 0);
        } catch(ex) {
            main.logException(ex, "Error in tibco.admin.Main.ongetImplementingPluginsError")
        }
    };

    /**
     * Internal function. Called when an error occurs in the service call to get implementing plugin components
     * for a given list of interfaces, due to the mapping being invalid. Notifies the requesting plugin container
     * of the error by returning <code>null</code> in the callback.
     *
     * @param objEvent {Object} the response object from the service call.
     * @private
     * @jsxobf-clobber
     */
    main.ongetImplementingPluginsInvalid = function(objEvent) {
        try {
            //        tibco.admin.util.PendingOperationsHandler.removeOperation(objEvent.target.opId);
            tibco.admin.util.ServiceHelper.printInboundOutboundDocument(objEvent);
            //        tibco.admin.util.ServiceHelper.logErrors('responseAddEnvironment399CDF', 'AddEnvironment', 'addEnvironment', objEvent);
            setTimeout(function() {
                objEvent.target.callbackFunction.call(objEvent.target.callbackTarget, null);
            }, 0);
        } catch(ex) {
            main.logException(ex, "Error in tibco.admin.Main.ongetImplementingPluginsInvalid")
        }
    };

    /**
     * Given an exception and an optional message, prints the exception to the logs
     *
     * @param ex {jsx3.lang.Exception} the exception to log
     * @param message {String} (optional) a message to prepend to the exception logging
     */
    main.logException = function(ex, message) {
        try {
            if (!ex.wasLogged) {
                ex.wasLogged = true;
                jsx3.util.Logger.GLOBAL.error((message) ? message : "An error occurred: ", jsx3.NativeError.wrap(ex));
            }
        } catch(e) {
            this.logException(new jsx3.lang.Exception("An error occurred while attempting to log an exception."), "Error logging exception: ");
        }
    }

    /**
     * Loads and parses a mapping rules file synchronously; returns a new instance of {tibco.admin.net.Service}.
     *
     * @param namespace {String | tibco.admin.util.component.ComponentConfig} the namespace of the plugin package that the resource belongs to.
     * @param strRulesURL {String} The relative URI for the rules document (a CXF document).
     * @param strOperationName {String} name of operation to call.
     * @param strPOIOperationName {String} name of process operation indicator.
     * @param callerInstanceId {String} (optional) a unique identifier for the instance of the caller. Ids are automatically generated
     *                    to uniquely identify the calling class/function for service calls to a given rules file/operation,
     *                    and ignore responses from earlier (stale) requests.  This is not able identify unique <i>instances</i>
     *                    of the calling class, however.  If you may have multiple instance of the same class, this handling
     *                    of stale requests must be done on an <i>instance</i> basis.  This parameter allows you to specify
     *                    an id for the unique instance of the calling class.
     * @param bNoCancelOnNew {boolean} if <code>true</code>, multiple requests for the same rules file/operation from the same
     *                    calling location will all be used (responses from earlier requests will not be ignored). (Default
     *                    value is <code>false</code>.
     *
     * @return {tibco.admin.net.Service} the service object that has been loaded.
     *
     * @throws {jsx3.lang.Exception} if an error occurs while creating the service object
     */
    main.loadService = function(namespace, strRulesURL, strOperationName, strPOIOperationName, callerInstanceId, bNoCancelOnNew) {
        try {
            jsx3.require("jsx3.net.Service");
            var objService = (new tibco.admin.net.Service(tibco.admin.Main.resolveURI(namespace, strRulesURL), strOperationName, strPOIOperationName, callerInstanceId, bNoCancelOnNew)).setNamespace(main.getServer());
            objService.setOutboundStubURL("jsx://xml/stubs/soap.xml")
            var stub = objService.getInboundURL()
            if (!tibco.admin.Main.getServer().getEnv("MODE")) {
                if (!stub) {
                    tibco.admin.util.Log.SERVICE.warn("System is running in static mode.\n Stub url for the mapping rule " + strOperationName + " specified at " + strRulesURL + " is " + stub);
                }
                else {
                    var absuri = tibco.admin.Main.resolveURI(namespace, stub)
                    tibco.admin.util.Log.SYSTEM.info("tibco.admin.Main#loadService: \n absolute path to mapping rule is " + absuri)
                    objService.setInboundURL(absuri);
                }
            }
            return  objService;
        }
        catch(e) {
            main.logException(e, "Error in tibco.admin.Main.loadService");
            throw new jsx3.lang.Exception("Error creating service object for rules file " + strRulesURL, jsx3.NativeError.wrap(e));
        }
    }

    /**
    * Opens the API documentation in a GI dialog.
    *
    * @param objEvent {jsx3.gui.Event} the event that triggered this action
    *
    * @private
    */
    main.apiHelpWindowDidOpen = function(objEvent) {
        jsx3.ide.LOG.debug("apiHelpWindowDidOpen " + objEvent.target.getName());

        var w = objEvent.target;
        if (w.getHelpBlock() == null) {
            var dialog = jsx3.IDE.getRootBlock().getChild('jsx_ide_api_dialog');
            if (dialog != null && dialog.getHelpBlock() != null) {
                jsx3.ide.LOG.debug("apiHelpWindowDidOpen adopting");
                w.getHelpBlockParent().adoptChild(dialog.getHelpBlock());
            } else {
                jsx3.ide.LOG.debug("apiHelpWindowDidOpen loading");
                try {
                    w.getHelpBlockParent().loadAndCache('jsxaddin://user!MatrixAdmin/components/apihelp/apihelp.xml');
                }
                catch(failed) {
                    try {
                        tibco.admin.Main.getServer().getCache().clearById(tibco.admin.Main.getServer().resolveURI('jsxaddin://user!MatrixAdmin/components/apihelp/apihelp.xml'));
                    }
                    catch(innerfail) {
                        // do nothing
                    }
                }
            }

            if (dialog)
                dialog.getParent().removeChild(dialog);
        }
    };

    /**
    * Opens the API documentation in GI (non-HTML format)
    *
    * @param bAsWindow {boolean} <code>true</code> indicates that it should open in a separate window, <code>false</code>
    *                            indicates it should open in an internal dialog within GI Builder.
    *
    * @private
    */
    main.doOpenApiHelp = function(bAsWindow) {
        jsx3.ide.LOG.debug("doOpenApiHelp window:" + bAsWindow);

        var w = jsx3.IDE.getAppWindow("api_help");
        var success = false;
        if (bAsWindow) {
            if (w == null) {
                w = jsx3.IDE.loadAppWindow('jsxaddin://user!MatrixAdmin/components/apihelp/as_window.xml');
                w.subscribe(jsx3.gui.Window.DID_OPEN, this.apiHelpWindowDidOpen);
                //          w.subscribe(jsx3.gui.Window.WILL_CLOSE, jsx3.ide.saveApiHelpWindowState);
                //          w.subscribe(jsx3.gui.Window.DID_FOCUS, jsx3.ide.saveApiHelpWindowState);
            }
            if (w.isOpen())
                w.focus();
            else
                w.open();

            success = w.isOpen();
        }

        if (! success) {
            var dialog = jsx3.IDE.getRootBlock().getChild('jsx_ide_api_dialog');
            jsx3.ide.LOG.debug("switching to dialog: " + dialog);

            if (dialog == null) {
                jsx3.ide.LOG.debug("doOpenApiHelp loading dialog component");
                try {
                    dialog = jsx3.IDE.getRootBlock().loadAndCache('jsxaddin://user!MatrixAdmin/components/apihelp/as_dialog.xml');
                }
                catch(failed) {
                    try {
                        tibco.admin.Main.getServer().getCache().clearById(tibco.admin.Main.getServer().resolveURI('jsxaddin://user!MatrixAdmin/components/apihelp/as_dialog.xml'));
                    }
                    catch(innerfail) {
                        // do nothing
                    }
                }
            }

            if (dialog.getHelpBlock() == null) {
                if (w != null && w.getHelpBlock() != null) {
                    jsx3.ide.LOG.debug("doOpenApiHelp adopting API help content");
                    dialog.getHelpBlockParent().adoptChild(w.getHelpBlock());
                } else {
                    jsx3.ide.LOG.debug("doOpenApiHelp loading API help content component");
                    window.setTimeout(function() {
                        try {
                            dialog.getHelpBlockParent().loadAndCache('jsxaddin://user!MatrixAdmin/components/apihelp/apihelp.xml');
                        }
                        catch(failed) {
                            try {
                                tibco.admin.Main.getServer().getCache().clearById(tibco.admin.Main.getServer().resolveURI('jsxaddin://user!MatrixAdmin/components/apihelp/apihelp.xml'));
                            }
                            catch(innerfail) {
                                // do nothing
                            }
                        }
                    }, 0);
                }
            }

            if (w != null && w.isOpen())
                w.close();

            window.setTimeout(function() {
                dialog.focus();
            }, 0);
        }
    }

    /**
    * Internal function to open an html document in a separate window.  Used to open the HTML help.
    *
    * @private
    */
    main.openConsoleWindow = function(strURL, strPopupName, intWidth, intHeight, yesnoScrollbar,
                                      yesnoMenuBar, yesnoStatus, yesnoLocation, yesnoToolbar, intLeft, intTop) {

        if (this.CONSOLES == null) this.CONSOLES = {};

        if (strPopupName) {
            try {
                var console = this.CONSOLES[strPopupName];
                if (console && ! console.closed) {
                    // timeout because closing a menu focuses the GI app again
                    window.setTimeout(function() {
                        console.focus();
                    }, 10);
                    return;
                }
            } catch (e) {
            }
        }

        var strScrollbar = yesnoScrollbar ? "scrollbars=" + yesnoScrollbar + "," : "";
        var strMenuBar = yesnoMenuBar ? "menubar=" + yesnoMenuBar + "," : "";
        var strStatus = yesnoStatus ? "status=" + yesnoStatus + "," : "";
        var strLocation = yesnoLocation ? "location=" + yesnoLocation + "," : "";
        var strToolbar = yesnoToolbar ? "toolbar=" + yesnoToolbar + "," : "";
        var strWidth = intWidth ? "width=" + intWidth + "," : "";
        var strHeight = intHeight ? "height=" + intHeight + "," : "";
        var strLeft = intLeft ? "left=" + intLeft + "," : "";
        var strTop = intTop ? "top=" + intTop + "," : "";

        this.CONSOLES[strPopupName] = window.open(this.getServer().resolveURI(strURL), strPopupName, "directories=no,resizable=yes," +
                                                                                                     strScrollbar + strMenuBar + strStatus + strLocation + strToolbar + strWidth + strHeight + strLeft + strTop);
        window.setTimeout(function() {
            tibco.admin.Main.CONSOLES[strPopupName].focus();
        }, 10);
        return this.CONSOLES[strPopupName];
    };

    /**
    * Checks to see if the current session has expired, and redirects the user to the login page if it has.
    *
    * @private
    */
    main._sessionExpired = function() {
        if (tibco.admin.Main.getServer().getEnv("MODE")) {
            if (tibco.admin.util.ServiceHelper.sessionExpired()) {
                tibco.admin.util.Log.SERVICE.warn("tibco.admin.Main#_sessionExpired: session is expired");
                tibco.admin.util.ServiceHelper.redirectToLoginPage();
                return true;
            }
        }
        return false;
    }

    /**
     * Show an alert dialog.  The signature and usage is identical to that in the standard GI alert function, but it gives
     * an alert dialog that uses the MatrixAdmin Visual Style.
     *
     * @param strTitle {String} the title of the dialog
     * @param strMessage {String} the message to display
     * @param fctOnOk {Function} callback function on pressing ok button, receives the dialog as an argument; if null the dialog will close itself; if defined must explicitly close the dialog
     * @param strOk {String} the text of the ok button, can be false to remove button from display
     * @param objParams {objParams} argument to configureAlert()
     */
    main.alert = function(strTitle, strMessage, fctOnOk, strOk, objParams) {
        var dialog = this.getServer().getBodyBlock().loadAndCache('jsxaddin://user!MatrixAdmin/components/alertDialog.xml', false);
        var ok = dialog.getDescendantOfName('ok');

        if(strTitle == null) {
            strTitle = this.getServer().getDynamicProperty("@addin@AlertDialog Default Title");
        }
        dialog.getDescendantOfName('title').setText(strTitle);
        if(strMessage == null) {
            strMessage = this.getServer().getDynamicProperty("@addin@AlertDialog Default Alert Text");
        }
        dialog.getDescendantOfName('message').setText(strMessage);

        if(strOk === false)
            dialog.hideButton();
        else {
            if(strOk == null) {
                strOk = this.getServer().getDynamicProperty("@addin@Btn OK");
            }
            ok.setText(strOk);
        }

        if(fctOnOk != null) {
            var onExecute = "_onExecute";
            ok._onExecute = fctOnOk;
            ok.setEvent("this." + onExecute + "(this.getAncestorOfType(jsx3.gui.Dialog));", jsx3.gui.Interactive.EXECUTE);
        }

        this.configureAlert(dialog, objParams);
        this.getServer().getBodyBlock().paintChild(dialog);
        dialog.focus();
        return dialog;
    }

    /**
     * Show a confirm dialog.  The signature and usage is identical to that in the standard GI confirm function, but it gives
     * a confirm dialog that uses the MatrixAdmin Visual Style.
     *
     * @param strTitle {String} the title of the dialog
     * @param strMessage {String} the message to display
     * @param fctOnOk {Function} callback function on pressing ok button, receives the dialog as an argument; if null the dialog will close itself; if defined must explicitly close the dialog
     * @param fctOnCancel {Function} callback function on pressing cancel button, receives the dialog as an argument; if null the dialog will close itself; if defined must explicitly close the dialog
     * @param strOk {String} the text of the ok button
     * @param strCancel {String} the text of the cancel button
     * @param intBtnDefault {int} the bold button that receives return key, 1:ok, 2:cancel, 3:no
     * @param fctOnNo {Function} callback function on pressing no button, receives the dialog as an argument; if null the dialog will close itself; if defined must explicitly close the dialog
     * @param strNo {String} the text of the no button
     * @param objParams {objParams} argument to configureAlert()
     */
    main.confirm = function(strTitle, strMessage, fctOnOk, fctOnCancel, strOk, strCancel, intBtnDefault,
                                        fctOnNo, strNo, objParams) {
        var dialog = this.getServer().getBodyBlock().loadAndCache('jsxaddin://user!MatrixAdmin/components/confirmDialog.xml', false);
        var ok = dialog.getDescendantOfName('ok');
        var cancel = dialog.getDescendantOfName('cancel');
        var no = dialog.getDescendantOfName('no');
        var buttons = [ok, cancel, no];
        intBtnDefault = intBtnDefault != null ? intBtnDefault - 1 : 1;

        if(strTitle == null) {
            strTitle = this.getServer().getDynamicProperty("@addin@ConfirmDialog Default Title");
        }
        dialog.getDescendantOfName('title').setText(strTitle);
        if(strMessage == null) {
            strMessage = this.getServer().getDynamicProperty("@addin@ConfirmDialog Default Alert Text");
        }
        dialog.getDescendantOfName('message').setText(strMessage);
        if(strOk == null) {
            strOk = this.getServer().getDynamicProperty("@addin@Btn OK");
        }
        ok.setText(strOk);
        if(strCancel == null) {
            strCancel = this.getServer().getDynamicProperty("@addin@Btn Cancel");
        }
        cancel.setText(strCancel);
        if(fctOnCancel != null) {
            var onExecute = "_onExecute";
            cancel._onExecute = fctOnCancel;
            cancel.setEvent("this." + onExecute + "(this.getAncestorOfType(jsx3.gui.Dialog));", jsx3.gui.Interactive.EXECUTE);
        }
        if(fctOnOk != null) {
            var onExecute = "_onExecute";
            ok._onExecute = fctOnOk;
            ok.setEvent("this." + onExecute + "(this.getAncestorOfType(jsx3.gui.Dialog));", jsx3.gui.Interactive.EXECUTE);
        }
        if(fctOnNo != null || strNo != null || intBtnDefault == 3) {
            if(strNo == null) {
                strNo = this.getServer().getDynamicProperty("@addin@Btn No");
            }
            no.setText(strNo);
            if(fctOnNo) {
                var onExecute = "_onExecute";
                no._onExecute = fctOnNo;
                no.setEvent("this." + onExecute + "(this.getAncestorOfType(jsx3.gui.Dialog));", jsx3.gui.Interactive.EXECUTE);
            }
            no.setDisplay(jsx3.gui.Block.DISPLAYBLOCK);
        }

        var defaultBtn = buttons[intBtnDefault];
        if(defaultBtn) {
            defaultBtn.setFontWeight(this.getServer().getDynamicProperty('@addin@Btn Font Weight'));

            dialog.registerHotKey(function(objEvent) {
                if(objEvent.enterKey()) {
                    this.getDescendantOfName(defaultBtn.getName()).doExecute(objEvent);
                    objEvent.cancelBubble();
                }
            }, jsx3.gui.Event.KEY_ENTER, false, false, false);
        }

        this.configureAlert(dialog, objParams);
        this.getServer().getBodyBlock().paintChild(dialog);
        dialog.focus();
        return dialog;
    }

    /**
     * Show a textbox input prompt dialog.  The signature and usage is identical to that in the standard GI prompt function, but it gives
     * a textbox input prompt dialog that uses the MatrixAdmin Visual Style.
     *
     * @param strTitle {String} the title of the dialog
     * @param strMessage {String} the message to display
     * @param fctOnOk {Function} callback function on pressing ok button, receives the dialog as an argument, and the value of the text input as a second argument; if null the dialog will close itself; if defined must explicitly close the dialog
     * @param fctOnCancel {Function} callback function on pressing cancel button, receives the dialog as an argument; if null the dialog will close itself; if defined must explicitly close the dialog
     * @param strOk {String} the text of the ok button
     * @param strCancel {String} the text of the cancel button
     * @param objParams {objParams} argument to configureAlert()
     */
    main.prompt = function(strTitle, strMessage, fctOnOk, fctOnCancel, strOk, strCancel, objParams) {
        var dialog = this.getServer().getBodyBlock().loadAndCache('jsxaddin://user!MatrixAdmin/components/promptDialog.xml', false);
        var ok = dialog.getDescendantOfName('ok');
        var cancel = dialog.getDescendantOfName('cancel');

        if(strTitle == null) {
            strTitle = this.getServer().getDynamicProperty("@addin@ConfirmDialog Default Title");
        }
        dialog.getDescendantOfName('title').setText(strTitle);
        if(strMessage == null) {
            strMessage = this.getServer().getDynamicProperty("@addin@ConfirmDialog Default Alert Text");
        }
        dialog.getDescendantOfName('message').setText(strMessage);
        if(strOk == null) {
            strOk = this.getServer().getDynamicProperty("@addin@Btn OK");
        }
        ok.setText(strOk);
        if(strCancel == null) {
            strCancel = this.getServer().getDynamicProperty("@addin@Btn Cancel");
        }
        cancel.setText(strCancel);
        if(fctOnOk != null) {
            var onExecute = "_onExecute";
            ok._onExecute = fctOnOk;
            ok.setEvent("var d = this.getAncestorOfType(jsx3.gui.Dialog); this." + onExecute + "(d, d.getDescendantOfName('value').getValue());", jsx3.gui.Interactive.EXECUTE);
        }
        if(fctOnCancel != null) {
            var onExecute = "_onExecute";
            cancel._onExecute = fctOnCancel;
            cancel.setEvent("this." + onExecute + "(this.getAncestorOfType(jsx3.gui.Dialog));", jsx3.gui.Interactive.EXECUTE);
        }

        this.configureAlert(dialog, objParams);
        this.getServer().getBodyBlock().paintChild(dialog);
        jsx3.sleep(function() {
            dialog.getDescendantOfName('value').focus();
        });
        return dialog;
    };

    /**
    * Internal function used to configure an alert dialog.
    *
    * @param objDialog {jsx3.gui.Dialog} the dialog to be configured.
    * @param objParams {Object} an object containing any of the following (optional) fields:
    *        <ul><li><code>width</code> - the width to set for the dialog</li>
    *        <li><code>height</code> - the height to set for the dialog</li>
    *        <li><code>noTitle</code> - if exists, hides the dialog's title bar (be sure to include a close button!</li>
    *        <li><code>nonModal</code> - if exists, makes sure the dialog is not modal</li></ul>

    * @private
    */
    main.configureAlert = function(objDialog, objParams) {
        if(objParams == null) return;

        if(objParams.width)
            objDialog.setWidth(objParams.width, false);
        if(objParams.height)
            objDialog.setHeight(objParams.height, false);

        if(objParams.noTitle)
            objDialog.removeChild(objDialog.getChild('title'));

        if(objParams.nonModal)
            objDialog.setModal(jsx3.gui.Dialog.NONMODAL);
    };

    /**
    * Utility function to apply a mask to a given block using a custom color. The mask can be hidden again via the usual
    * means (call the <code>hideMask</code> function directly on the block).
    *
    * @param block {jsx3.gui.Block} the block to apply the mask to.
    * @param strColor {String} the string representation of the color to use for the mask.  All tthe standard javascript
    *                          string representations of colors are supported (named colors, Hex code, etc.).
    */
    main.showMask = function(block, strColor) {
        block.showMask();
        var arr = block.getChildren();
        var child = arr[arr.length - 1];
        child.setDynamicProperty("jsxbgcolor", null);
        if(!strColor) {
            strColor = "#666666";
        }
        child.setBackgroundColor(strColor, true);
    }

    if(!jsx3.net.URIResolver.DEFAULT.resolveURI_old) {
        var uriResolver = jsx3.net.URIResolver.DEFAULT;
        uriResolver.resolveURI_old = uriResolver.resolveURI;
        /**
         * Resolves a URI that is referenced from a file in this server. This method takes into account the changes in
         * resource addressing between 3.1 and 3.2. For version 3.1, the URI is resolved as any URI in the system, using
         * <code>jsx3.resolveURI()</code>. In version 3.2, the URI is taken as relative to the application folder. In
         * particular, a relative URI will be resolved to a base of the application folder, an absolute URI will be
         * unaffected.
         * @param strURI {String|jsx3.net.URI} the URI to resolve.
         * @return {jsx3.net.URI} the resolved URI.
         * @since 3.2
         * @see jsx3.net.URIResolver
         * @see jsx3#resolveURI()
         */
        uriResolver.resolveURI = function(strURI) {
            var scheme = "amxplugin://";
            // if the new amxplugin:// scheme is used in the URI
            if(strURI.toString().indexOf(scheme) == 0) {
                // to get the namespace + path, strip off the scheme from the raw URI
                var namespace = strURI.toString().substring(scheme.length);
                // strip off any leading slashes (thus it allows both amxplugin:// and amxplugin:///
                while(namespace.indexOf("/") == 0) {
                    namespace = namespace.substring(1);
                }

                // find the index of the first slash (occurs right after the namespace)
                var idx = namespace.indexOf("/");

                // relative path is after the slash at idx
                var relPath = namespace.substring(idx + 1);
                // namespace is everything up to the slash at idx
                namespace = namespace.substring(0,idx);

                // resolve the path using the plugin namespace resolver in the Main class
                return new jsx3.net.URI(tibco.admin.Main.resolveURI(namespace, relPath));
            }
            else {
                // otherwise use the GI standard resolver function
                return uriResolver.resolveURI_old(strURI);
            }
        }
    }

    main.loadAddin = function(namespace, addinPath) {
        // modify/tweak the System class to support our customized addin keys, if it's not done already
        if(!jsx3.System.customKeys) {
            jsx3.System.customKeys = new Object();
            jsx3.System.oldGetLoadedAddinByKey = jsx3.System.getLoadedAddinByKey;
            jsx3.System.getLoadedAddinByKey = function(strKey) {
                var newKey = jsx3.System.customKeys[strKey];
                return jsx3.System.oldGetLoadedAddinByKey(newKey ? newKey : strKey);
            };
        }

        // remove any leading slash
        if(addinPath.indexOf("/") == 0) {
            addinPath = addinPath.substring(1);
        }

        // get the name of the addin (the name of the last folder in the path)
        var name = addinPath;
        var cutAt = addinPath.lastIndexOf("/");
        if(cutAt == (addinPath.length - 1)) {
            // if there is a trailing slash, cut it off first
            addinPath = addinPath.substring(0, cutAt);
            cutAt = addinPath.lastIndexOf("/");
        }
        if(cutAt != -1) {
            name = addinPath.substring(cutAt + 1);
        }
        else {
            name = addinPath;
        }

        // if the addin name is not already registered, load the addin
        if(!jsx3.System.getLoadedAddinByKey("user:" + name)) {
            // resolve the path to the context roots' parent folder, relative to the user addins folder
            var defaultAddinStr = APP.tibco.admin.resolveURI("jsxaddin://user!MatrixAdmin").toString();
            var userStr = APP.tibco.admin.resolveURI("jsxuser://").toString();
            defaultAddinStr = defaultAddinStr.substring(defaultAddinStr.lastIndexOf(userStr) + userStr.length);
            var addinsIdx = defaultAddinStr.lastIndexOf("addins");
            defaultAddinStr = defaultAddinStr.substring(0, addinsIdx + 6);
            var lastIdx = 0;
            var nextSlashIdx = defaultAddinStr.indexOf("/", lastIdx);
            var relPathFromAddinsFolder = "";
            while((nextSlashIdx != -1) && (nextSlashIdx < addinsIdx)) {
                relPathFromAddinsFolder += "../";
                lastIdx = nextSlashIdx + 1;
                nextSlashIdx = defaultAddinStr.indexOf("/", lastIdx);
            }
            if(tibco.admin.Main.isFileSystem()) {
                relPathFromAddinsFolder += "../";
            }
            else {
                relPathFromAddinsFolder += "../../";
            }

            // get the path to the addin, relative to the user addins folder
            var pathToAddin = tibco.admin.Main.getContextRootForNamespace(namespace) + "/" + addinPath;
            if(tibco.admin.Main.isFileSystem()) {
                path = relPathFromAddinsFolder + "JSXAPPS/" + pathToAddin;
            }
            else {
                path = relPathFromAddinsFolder + pathToAddin;
            }

            // get the path to use for the addin's URIResolver (relative to launch page for filesystem, absolute path
            // deployed project)
            var loc = window.location.toString();
            var index = loc.lastIndexOf("?");
            if(index > -1) {
                // strip off any parameters after the html file...
                loc = loc.substring(0, index);
            }
            // strip off the html file, etc
            loc = loc.substring(0, loc.lastIndexOf("/"));
            if(!tibco.admin.Main.isFileSystem()) {
                // live mode is one folder further down the hierarchy, so relative path must go up one more folder
                loc = loc.substring(0, loc.lastIndexOf("/"));
            }
            var absPath = loc + "/" + pathToAddin;
            if(tibco.admin.Main.isFileSystem()) {
                absPath = tibco.admin.Main.getServer().resolveURI("jsxuser://") + "JSXAPPS" + "/" + pathToAddin;
            }

            // create the Addin object
            var addin = new jsx3.app.AddIn("user:" + path);
            addin.myUri = new jsx3.net.URI(absPath + "/");
            // the custom addin URI to resolved with
            addin.resolveURI = function(strURI) { // override to use customized URIResolver
                var uri = jsx3.net.URI.valueOf(strURI);
                if(jsx3.util.compareVersions(this.getJsxVersion(), "3.2") >= 0 && !jsx3.net.URIResolver.isAbsoluteURI(uri)) {
                    return jsx3.net.URIResolver.DEFAULT.resolveURI(this.myUri.resolve(uri));
                }
                else {
                    return jsx3.net.URIResolver.DEFAULT.resolveURI(uri);
                }
            };

            // load the addin
            jsx3.CLASS_LOADER.loadAddin(addin);
            // push any dynamic properties just loaded to the Server object's JSS and LJSS Properties objects
            APP.tibco.admin.getProperties().addParent(jsx3.System.LJSS);
            APP.tibco.admin.getProperties().addParent(jsx3.System.JSS);
            // register the short name of this addin (the long relative path was registered by the system)
            jsx3.System.customKeys["user:" + name] = "user:" + path;
        }
        else {
            jsx3.warn("Cannot add addin....addin key already exists: 'user:" + name + "'");
        }
    }

   // todo: Remove main._setSystemLocale once this GI bug is fixed.
   /**
     * Internal function. Due to a bug in GI 3.5 the property bundles in addin will not load.
     * This function quarantines that the property bundle addins\MatrixAdmin\jss\adminJSS.xml
     * is loaded properly.
     * @private
     * @jsxobf-clobber
     */
    main._setSystemLocale = function() {
        //alert(tibco.admin.Matrix.ADDIN.resolveURI("jss/adminJSS.xml") )
        main.pb = jsx3.app.PropsBundle.getProps(tibco.admin.Matrix.ADDIN.resolveURI("jss/adminJSS.xml"), null, jsx3.getSystemCache());
        jsx3.System.LJSS.addParent(main.pb);
    }

    main._setSystemLocale();


    // todo: Remove main._setLocale once this GI bug is fixed.
    /**
     * Internal function. Due to a bug in GI 3.5 the property bundles in addin will not load.
     * This function quarantines that the appropriate language of the property bundle
     * addins\MatrixAdmin\jss\adminJSS.xml is loaded properly.
     * Use this function only if the user can interact with the interface to change the system language to a preferred language
     *
     * @param newLocale {jsx3.util.Locale} new Locale
     */
    main.setLocale = function(newLocale) {
        jsx3.System.LJSS.removeParent(main.pb);
        main.pb = jsx3.app.PropsBundle.getProps(tibco.admin.Matrix.ADDIN.resolveURI("jss/adminJSS.xml"), newLocale, jsx3.getSystemCache());
        jsx3.System.LJSS.addParent(main.pb);
    }





});

if (jsx3.IDE) {
    var menubar = jsx3.IDE.getJSXByName("jsxmenubar");
    var cdfXML = "<data jsxid='jsxroot'><record config='true' jsxexecute='tibco.admin.Main.doOpenApiHelp(false);' jsxid='2' jsxtext='Open API (in dialog)'/><record config='true' jsxexecute='tibco.admin.Main.doOpenApiHelp(true);' jsxid='3' jsxtext='Open API (in separate window)'/><record config='true' jsxexecute='tibco.admin.Main.openConsoleWindow(&quot;jsxaddin://user!MatrixAdmin/doc/html/api/index.html&quot;,&quot;jsxide_api&quot;,null,null,&quot;yes&quot;,&quot;yes&quot;,&quot;yes&quot;,&quot;yes&quot;,&quot;yes&quot;);' jsxid='4' jsxtext='...HTML version'/></data>";
    var menu = new jsx3.gui.Menu("tibcoIDEMenu", "ActiveMatrix Plugin Framework Help");
    menu.setXMLString(cdfXML);
    menubar.setChild(menu);
    menubar.paintChild(menu);
}
